/* -*- C++ -*- */

//=============================================================================
/*
* @author Feloweria <380374483@qq.com>
* Copyright (C) Feloweria
*
* $ID 2014-05-14 11:25:19 AM
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* @version 1.0
*/
//=============================================================================


#include "Network/SocketManager.h"
#include "Network/Acceptor.h"
#include "Base/StringConvertor.h"

#ifdef __LINUX__
	#include <unistd.h>
	#include <netdb.h>
	#include <arpa/inet.h>
	#include <sys/socket.h>
#endif


FELOWRIA_BEGIN

Acceptor::Acceptor(const char *addr, int32 port)
:listen_sock(0)
{
	listen_sock = ::socket(AF_INET, SOCK_STREAM, 0);
	sockaddr_in listen_addr;
	listen_addr.sin_family = AF_INET;
	listen_addr.sin_port = ntohs((uint16)port);
	listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	uint32 option = 1;
	setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&option, 4);

	if(strcmp(addr, "0.0.0.0"))
	{
	      hostent* hostname = gethostbyname(addr);
	      if(hostname != 0)
		 memcpy(&listen_addr.sin_addr.s_addr, hostname->h_addr_list[0], hostname->h_length);
	}

	if(::bind(listen_sock, (const sockaddr*)&listen_addr, sizeof(sockaddr_in)))
	    return ;
	
	while(listen(listen_sock,5))
		LOG_ERROR("listen " + String(addr) + ":" + StringConvertor<UTF8>::ToString(port) + " fail");
	
	LOG_TRACE("listen " + String(addr) + ":" + StringConvertor<UTF8>::ToString(port) + " success");
}

Acceptor::~Acceptor()
{

#ifdef __LINUX__
	shutdown(listen_sock, SHUT_RDWR);
	close(listen_sock);
#endif

#ifdef __WINDOWS__
	shutdown(listen_sock, SD_BOTH);
	closesocket(listen_sock);
#endif
}


bool Acceptor::Excute()
{
	sockaddr_in peer_addr = {0};

#ifdef __LINUX__
	socklen_t size = 0;
#endif

#ifdef __WINDOWS__
	int32 size = sizeof(sockaddr);
#endif

	int64 sock = accept(listen_sock,(sockaddr *)&peer_addr,&size);

	if(-1 == sock)
		return false;

	Socket *socket = Alloc();
	if (!socket)
	{
#ifdef __LINUX__
		      close(sock);
#endif

#ifdef __WINDOWS__
			  closesocket(sock);
#endif
		      return false;
	      
	}

	const char *ip = inet_ntoa(peer_addr.sin_addr);
	int32 port = ntohs(peer_addr.sin_port);

	if(!ip)
		ip = "Unknown";

	
	SocketThread *thread = SocketManager::Instance()->GetFreeThread();
	
	if(!thread)
		return false;
	
	socket->Reset();
	socket->OnAccept(thread,sock,ip,port);
	OnAccept(socket);

	return true;
}

FELOWRIA_END
